namespace SimpleWebServices.Web.Mvc
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Net;
    using System.Security.Principal;
    using System.Threading;
    using System.Web;
    using System.Web.Mvc;
    using SimpleWebServices.Configuration;

    [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
    public sealed class IsInRoleAttribute : ActionFilterAttribute
    {
        private string _roles;
        private string _schemes;

        public IsInRoleAttribute(string roles, string schemes)
        {
            this.Roles = roles;
            this.Schemes = schemes;
        }

        public string Roles
        {
            get
            {
                return this._roles;
            }

            private set
            {
                if (null == value)
                {
                    throw new ArgumentNullException("value");
                }
                else if (0 == value.Length)
                {
                    throw new ArgumentOutOfRangeException("value");
                }

                this._roles = value;
            }
        }

        public string Schemes
        {
            get
            {
                return this._schemes;
            }

            private set
            {
                if (null == value)
                {
                    throw new ArgumentNullException("value");
                }
                else if (0 == value.Length)
                {
                    throw new ArgumentOutOfRangeException("value");
                }

                this._schemes = value;
            }
        }

        private IList<IWwwAuthenticate> AuthenticationSchemes
        {
            get
            {
                List<IWwwAuthenticate> value = new List<IWwwAuthenticate>();

                if (!string.IsNullOrEmpty(this.Schemes))
                {
                    string[] keys = this.Schemes.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (string key in keys)
                    {
                        value.Add(ServiceLocator.Resolve<IWwwAuthenticate>(key));
                    }
                }

                return value;
            }
        }

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (null == filterContext)
            {
                throw new ArgumentNullException("filterContext");
            }

            base.OnActionExecuting(filterContext);

            if (this.Deny(filterContext.HttpContext.User))
            {
                Thread.CurrentThread.CurrentUICulture = new CultureInfo("en");

                filterContext.Result = (ViewResult)ServiceLocator.Resolve<IViewResult>("Unauthorized");
                
                HttpResponseBase response = filterContext.HttpContext.Response;
                response.Clear();
                response.StatusCode = (int)HttpStatusCode.Unauthorized;
                response.Cache.SetCacheability(HttpCacheability.NoCache);
                foreach (var scheme in this.AuthenticationSchemes)
                {
                    response.Headers.Add("WWW-Authenticate", scheme.Challenge(filterContext.HttpContext));
                }
            }
        }

        private bool Deny(IPrincipal principal)
        {
            bool result = true;

            if (null != principal && principal.Identity.IsAuthenticated)
            {
                string[] roles = this.Roles.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (string role in roles)
                {
                    if (principal.IsInRole(role))
                    {
                        result = false;
                        break;
                    }
                }
            }

            return result;
        }
    }
}